package com.zc.portal.infra.security.filter;

import com.alibaba.fastjson.JSON;
import com.zc.commons.pojo.ResponseData;
import com.zc.portal.domain.dto.SysRole;
import com.zc.portal.domain.dto.SysUser;
import com.zc.portal.infra.security.http.ReqHandleWrapper;
import com.zc.portal.infra.security.jwt.SecurityJwtUtils;
import io.jsonwebtoken.Claims;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;

public class JwtCheckAuthFilter extends BasicAuthenticationFilter {

    public JwtCheckAuthFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {

        try {
            String tokenHeader = request.getHeader(SecurityJwtUtils.TOKEN_HEADER);
            if (tokenHeader == null) {
                throw new BadCredentialsException("缺失认证信息！");
            }
            String token = tokenHeader.replace(SecurityJwtUtils.TOKEN_PREFFIX, "");
            UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(token);
            if (authenticationToken == null) {
                chain.doFilter(request, response);
                return;
            }
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        } catch (AuthenticationException e) {
            SecurityContextHolder.clearContext();
            this.onUnsuccessfulAuthentication(request, response, e);
            return;
        }
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthenticationToken(String token) {
        Claims claims = null;
        try {
            claims = SecurityJwtUtils.getTokenBody(token);
        } catch (Exception e) {
            throw new BadCredentialsException("The token is a bad Credential!", e);
        }
        // 解析用户信息
        String jsonStr = (String) claims.get(SecurityJwtUtils.USER_CLAIMS);
        Collection<String> roles = (Collection) claims.get(SecurityJwtUtils.ROLE_CLAIMS);
        Set<SysRole> sysRoles = roles.stream().map(role -> {
            SysRole sysRole = new SysRole();
            sysRole.setRoleCode(role);
            return sysRole;
        }).collect(Collectors.toSet());
        SysUser sysUser = JSON.parseObject(jsonStr, SysUser.class);
        sysUser.setRoleList(sysRoles);
        if (sysUser != null) {
            return new UsernamePasswordAuthenticationToken(sysUser, null, sysRoles);
        }
        return null;
    }

    @Override
    protected void onUnsuccessfulAuthentication(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException {
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json");
        ResponseData<String> responseData = new ResponseData();
        responseData.setCode(HttpStatus.UNAUTHORIZED.value());
        responseData.setMessage("凭证不可用！");
        responseData.setData(e.getMessage());
        String jsonString = JSON.toJSONString(responseData);
        PrintWriter printWriter = resp.getWriter();
        printWriter.write(jsonString);
        if (printWriter != null) {
            printWriter.flush();
            printWriter.close();
        }
    }
}
